#include "lims/fileIO/FileIO.hpp"
#include "lims/caculateRef/ChooseRef.h"
#include "lims/caculateRef/CaculateIValue.h"
#include "lims/entities/Reference.hpp"
#include "lims/queryProcessing/RangeQuery.h"
#include "lims/queryProcessing/PointQuery.h"
#include "lims/queryProcessing/RangeQuery_IO.h"
#include "lims/queryProcessing/PointQuery_IO.h"
#include "lims/queryProcessing/KNN.h"
#include "lims/queryProcessing/KNN_IO.h"
#include "lims/common/CalCirclePos.h"

#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <chrono>
#include <ctime>
#include <sstream>
#include <queue>
#include <algorithm>

#include "lims/dis.h"
// #include "dis_string.h"

#define rand() ((rand()%10000)*10000+rand()%10000)

using namespace std;
using namespace MetricSpaceBenchmark::MLIndex::LIMS;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 计算两个 Point 类型对象（point_a 和 point_b）之间的欧几里得距离
double CaculateEuclideanDis(Point &point_a, Point &point_b) {
    double total = 0.0;
    for (unsigned i = 0; i < point_a.coordinate.size(); i++) {
        total += pow(point_a.coordinate[i] - point_b.coordinate[i], 2);
    }
    return sqrt(total);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 两个点的欧式距离: 参数类型重载
double CaculateEuclideanDis2(vector<double> &point_a, Point &point_b) {
    double total = 0.0;
    for (unsigned i = 0; i < point_a.size(); i++) {
        total += pow(point_a[i] - point_b.coordinate[i], 2);
    }
    return sqrt(total);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 从指定的文件中读取数据，并将其解析为 Point 类型的数据点，存储在一个 vector<Point> 容器中返回
vector<Point> LoadPointForQuery(const string &filename) {
    ifstream fin;
    vector<Point> queryPoints;
    fin.open(filename.c_str());
    if (!fin) {
        cout << filename << " file could not be opened\n";
        exit(0);
    }
    string line;
    while (getline(fin, line)) {
        stringstream ss(line);
        string value;
        vector<double> coordinate;
        while (getline(ss, value, ','))
            coordinate.push_back(atof(value.c_str()));

        Point point = Point(coordinate);
        queryPoints.push_back(point);
    }
    fin.close();
    return queryPoints;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 按照 InsertPt 类型对象的 i_value 属性进行升序排序
static bool AscendingSort(const InsertPt &point_a, const InsertPt &point_b) {
    return point_a.i_value < point_b.i_value;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
 * 主要功能是从给定的文件中读取数据点信息，计算这些数据点与多个参考点之间的距离，根据距离将数据点分配到不同的簇（cluster）中，
 * 然后对每个簇内的数据点按照特定属性（i_value）进行升序排序，最后将每个簇内排序好的数据点分页存储到二进制文件中，方便后续的数据处理与查询等操作。
 */
void Insert(const string &filename, vector<mainRef_Point> &all_ref, unsigned num_clu) {
    ifstream fin;
    vector<InsertPt> insertPt;
    fin.open(filename);
    if (!fin) {
        cout << filename << " file could not be opened\n";
        exit(0);
    }
    string line;
    unsigned id = 0;
    while (getline(fin, line)) {
        stringstream ss(line);
        string value;
        vector<double> coordinate;
        while (getline(ss, value, ','))
            coordinate.push_back(atof(value.c_str()));

        InsertPt point = InsertPt(coordinate, id++);
        insertPt.push_back(point);
    }
    fin.close();

    int insert_size = insertPt.size();
    // 创建一个二维vector<InsertPt>类型的容器all_insertPt，用于按照簇的划分来存储数据点，其大小调整为num_clu（表示簇的数量），每个内层向量对应一个簇，初始时每个内层向量都是空的，后续会将数据点分配到相应的簇向量中
    vector<vector<InsertPt> > all_insertPt;
    all_insertPt.resize(num_clu);

    for (int index = 0; index < insert_size; ++index) {
        // 计算当前数据点（insertPt[index]）与第一个参考点（all_ref[0].point，假设point是mainRef_Point类中存储数据点相关信息的成员变量，类型合适用于距离计算）之间的距离，使用CaculateEuclideanDis2函数进行计算，将结果赋值给dis变量，这里先以第一个参考点为基准来比较距离
        double dis = CaculateEuclideanDis2(insertPt[index].coordinate, all_ref[0].point);
        // 初始化一个变量clu_id，用于记录当前数据点距离最近的簇的标识符，初始设为0，也就是先假设当前数据点距离第一个簇最近，后续会通过比较进行更新
        int clu_id = 0;
        // 遍历除第一个参考点之外的其他参考点（通过循环，从i = 1到i < num_clu），计算当前数据点与这些参考点的距离，并与之前记录的最小距离进行比较，更新最小距离以及对应的簇标识符
        for (unsigned i = 1; i < num_clu; ++i) {
            // 计算当前数据点（insertPt[index]）与当前遍历的参考点（all_ref[i].point）之间的距离，使用CaculateEuclideanDis2函数进行计算，将结果赋值给dis_oth变量
            double dis_oth = CaculateEuclideanDis2(insertPt[index].coordinate, all_ref[i].point);
            // 如果当前计算得到的距离dis_oth小于之前记录的最小距离dis，说明当前参考点对应的簇距离当前数据点更近，更新最小距离为dis_oth，同时更新簇标识符为当前遍历的簇索引i
            if (dis_oth < dis) {
                dis = dis_oth;
                clu_id = i;
            }
        }
        // 将当前数据点的距离信息（也就是距离最近的参考点的距离）设置到InsertPt对象的相关属性中（通过调用setIValue函数，假设InsertPt类有这样的成员函数用于设置该属性），方便后续基于这个距离值进行排序等操作
        insertPt[index].setIValue(dis);
        // 将当前数据点添加到距离它最近的簇对应的向量中（all_insertPt[clu_id]），完成数据点到簇的分配操作
        all_insertPt[clu_id].push_back(insertPt[index]);
    }

    // 遍历每个簇（通过循环，从index = 0到index < num_clu），对每个簇内的数据点进行以下操作，包括排序以及分页存储到二进制文件中
    for (unsigned index = 0; index < num_clu; ++index) {
        // 使用标准库中的sort函数对当前簇（all_insertPt[index]）内的数据点按照AscendingSort函数定义的规则（即按照InsertPt类型对象的i_value属性进行升序排序）进行排序，使得簇内数据点按照特定顺序排列，方便后续处理
        sort(all_insertPt[index].begin(), all_insertPt[index].end(), AscendingSort);
        // 将排序好的当前簇的数据点设置到对应的mainRef_Point对象的相关属性中（通过调用all_ref[index].setInsertPt函数，假设mainRef_Point类有这样的成员函数用于设置插入点列表等相关属性），更新参考点对象中存储的簇内数据点信息
        all_ref[index].setInsertPt(all_insertPt[index]);

        // 创建一个输出文件流对象fout，用于后续将数据点分页写入二进制文件中
        ofstream fout;
        // 计算当前簇内数据点分页后的页数，通过将簇内数据点数量（all_ref[index].insert_list.size()，这里假设insert_list是mainRef_Point类中存储簇内插入点列表的成员变量）除以Constants::PAGE_SIZE（应该是一个常量表示每页可容纳的数据点数量）并向上取整（使用ceil函数）得到，结果赋值给page_num变量
        unsigned page_num = ceil((double) all_ref[index].insert_list.size() / Constants::PAGE_SIZE);
        // 初始化一个计数器变量count，用于记录已经写入文件的数据点数量，初始值设为0，后续会随着数据点的写入不断更新，确保正确分页写入数据
        unsigned count = 0;
        // 获取当前簇内第一个数据点的坐标维度数量（通过访问all_ref[index].insert_list[0].coordinate.size()，假设coordinate是InsertPt类中存储坐标值的成员变量，类型为vector<double>），将其赋值给size_dim变量，用于后续写入文件时确定每次写入的数据长度等操作
        int size_dim = all_ref[index].insert_list[0].coordinate.size();
        // 遍历每一页（通过循环，从i = 0到i < page_num），进行以下操作，将当前簇内的数据点分页写入二进制文件中
        for (unsigned i = 0; i < page_num; i++) {
            // 构造当前页对应的输出文件名，文件名格式为"./data/cluster_" + 当前簇索引的字符串表示（通过to_string函数将index转换为字符串） + "_insert_" + 当前页索引的字符串表示（同样通过to_string函数将i转换为字符串），将构造好的文件名赋值给outputFileName变量
            string outputFileName = "./data/cluster_" + to_string(index) + "_insert_" + to_string(i);
            // 以二进制模式打开构造好的文件名对应的文件，如果文件不存在则会创建新文件，如果文件已存在则会覆盖原有内容，准备写入数据
            fout.open(outputFileName, ios::binary);
            // 计算当前页实际要写入的数据点数量size_page，通过判断如果已经写入的数据点数量count加上每页固定的数据点数量Constants::PAGE_SIZE超过了当前簇内数据点总数（all_ref[index].insert_list.size()），则当前页写入的数据点数量为剩余的数据点数量（all_ref[index].insert_list.size() - count），否则就写入每页固定的数据点数量Constants::PAGE_SIZE，将计算结果赋值给size_page变量
            int size_page = count + Constants::PAGE_SIZE > all_ref[index].insert_list.size() ?
                            all_ref[index].insert_list.size() - count : Constants::PAGE_SIZE;
            // 将当前页要写入的数据点数量size_page以二进制形式写入文件，通过将其地址强制转换为const char *类型，并指定写入的字节数为4（假设size_page是int类型，在常见的系统中int类型占4个字节），使用fout.write函数进行写入操作
            fout.write((const char *) &size_page, 4);
            // 将数据点的坐标维度数量size_dim以二进制形式写入文件，同样将其地址强制转换为const char *类型，并指定写入的字节数为4，使用fout.write函数进行写入操作，方便后续读取文件时能正确解析数据点的坐标信息
            fout.write((const char *) &size_dim, 4);
            // 遍历当前页要写入的每个数据点（通过循环，从j = 0到j < size_page），将数据点的坐标值以二进制形式写入文件，通过将坐标值的首地址（通过调用coordinate.data()获取，假设coordinate是vector<double>类型，data()函数返回指向其内部存储数据的首地址）强制转换为char *类型（使用reinterpret_cast进行强制类型转换），并根据坐标维度数量和每个坐标值的字节大小（通过sizeof计算all_ref[index].insert_list[count + j].coordinate.front()的字节大小，假设front()函数返回vector<double>中的第一个元素，也就是一个double类型的值，获取其字节大小来确定每次写入的字节长度）确定写入的字节数（size_dim * sizeof(...)），使用fout.write函数进行写入操作，完成数据点坐标值的写入
            for (int j = 0; j < size_page; ++j)
                fout.write(reinterpret_cast<char *>(all_ref[index].insert_list[count + j].coordinate.data()),
                           size_dim * sizeof(all_ref[index].insert_list[count + j].coordinate.front()));
            // 关闭当前页对应的文件，释放相关资源，完成一页数据的写入操作后进行关闭，准备下一次打开新的文件写入下一页数据
            fout.close();
            // 更新已经写入文件的数据点数量计数器count，将其增加当前页实际写入的数据点数量size_page，为下一页数据的写入做准备，确保正确分页写入数据
            count += Constants::PAGE_SIZE;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cluster evaluate method
// 记录各元素到参考点集合的距离
double dist[NUUM];
int fgg[NUUM];    // 标记元素是否已选
int stack1[NUUM], top = 0;

double sm(double a, double b) {
    if (a > b) return b;
    return a;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 基于距离来逐步构建一组参考点（或可理解为聚类中心）
void work(int dim) {
    // 将栈顶指针 `top` 初始化为1，表示栈中即将放入第一个元素
    top = 1;
    // 随机选择一个在 `0` 到 `num`（这里 `num` 应该是在代码其他地方定义的一个表示数量的常量或者变量）范围内的整数作为初始元素，将其放入栈 `stack1` 中，作为后续操作的起始点
    stack1[0] = rand() % num;
    // 将对应位置的 `fgg` 数组元素标记为 `1`，表示该元素（对应索引为 `stack1[0]` 的那个元素）已经被处理过或者选中了，`fgg` 数组可能用于标记各个元素的某种状态（比如是否已参与聚类等）
    fgg[stack1[0]] = 1;
    // 通过循环遍历所有的元素（从 `j = 0` 到 `j < num`），计算每个元素与初始选中元素（`stack1[0]`）之间的距离，距离的计算通过调用 `dis` 函数（这里 `dis` 函数应该是在代码其他地方定义的，用于根据维度 `dim` 等信息计算两点间距离之类的功能），并将计算得到的距离值存储在 `dist` 数组中，`dist` 数组用于记录每个元素到某个参考元素的距离情况
    for (int j = 0; j < num; j++) dist[j] = dis(j, stack1[0], dim);
    // 通过循环选择 `pnum`（同样应该是代码其他地方定义的表示数量的变量） - 1 个元素，进行以下操作，逐步构建一个类似聚类中心或者参考点集合的过程
    for (int i = 1; i < pnum; i++) {
        // 初始化一个变量 `max`，用于记录当前未被选中元素中距离参考点最远的距离，初始值设为 `0`，后续会不断更新找到真正的最大距离对应的元素
        double max = 0;
        // 初始化一个变量 `k`，用于记录距离参考点最远的那个未被选中元素的索引，初始值设为 `-1`，后续通过比较距离来确定其准确值
        int k = -1;
        // 通过循环遍历所有元素（从 `j = 0` 到 `j < num`），查找未被选中（即 `fgg[j] == 0`）的元素中距离当前参考点集合最远的那个元素，进行以下操作
        for (int j = 0; j < num; j++)
            if (fgg[j] == 0) {
                // 如果当前元素的距离（`dist[j]`）大于当前记录的最大距离 `max`，则更新最大距离为当前元素的距离，同时更新最远元素的索引 `k` 为当前元素的索引 `j`
                if (dist[j] > max) {
                    max = dist[j];
                    k = j;
                }
            }
        // 将找到的最远元素的索引放入栈 `stack1` 中，作为新的参考点或者聚类中心之类的元素，同时栈顶指针 `top` 自增 `1`，表示栈中元素数量增加了一个
        stack1[top++] = k;
        // 将对应位置的 `fgg` 数组元素标记为 `1`，表示该元素（索引为 `k` 的元素）已经被选中，更新其状态
        fgg[k] = 1;
        // 通过循环遍历所有元素（从 `j = 0` 到 `j < num`），更新每个未被选中元素（`fgg[j] == 0`）到当前参考点集合的距离，取之前记录的距离 `dist[j]` 和当前元素与新加入的参考点（索引为 `k`）之间的距离（通过 `dis` 函数计算得到）中的较小值（通过调用 `sm` 函数实现取小值操作）作为新的距离值，更新 `dist` 数组中的对应元素，这样可以保证 `dist` 数组始终记录着每个元素到当前参考点集合最近的距离情况
        for (int j = 0; j < num; j++)dist[j] = sm(dist[j], dis(j, k, dim));
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 根据簇心划分数据集
void clu(int dim, const string &path) {
    vector<vector<int> > cluster;
    cluster.resize(pnum);
    for (int index = 0; index < num; ++index) {
        double dist = dis(index, stack1[0], dim);
        int pivot_index = 0;
        for (int i = 1; i < pnum; ++i) {
            double dis_o = dis(index, stack1[i], dim);
            if (dis_o < dist) {
                dist = dis_o;
                pivot_index = i;
            }
        }
        cluster[pivot_index].push_back(index);
    }
    ofstream fout;
    fout.setf(ios::fixed);
    fout.precision(8);

    for (int i = 0; i < pnum; ++i) {
        int len = cluster[i].size();

        if (len < 3) {
            continue;
        }
        string filename = path + "/clu_" + to_string(i) + ".txt";
        fout.open(filename);
        for (int j = 0; j < len; ++j) {
            fout << cluster[i][j];
            for (int d = 0; d < dim; ++d)
                fout << "," << loc[cluster[i][j]][d];
            fout << endl;
        }

        fout.close();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 保存簇心
void op(const string &filename, int dim) {
    ofstream out1;
    out1.open(filename);
    out1.setf(ios::fixed);
    out1.precision(8);

    for (int i = 0; i < top; i++) {
        out1 << loc[stack1[i]][0];
        for (int d = 1; d < dim; ++d)
            out1 << "," << loc[stack1[i]][d];
        out1 << endl;
    }
    out1.close();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double
CalErrAvg(vector<Clu_Point> &data, vector<vector<Point> > &ref_data, vector<double> &dist_max, int k, int num_ref,
          int num_alldata) {
    double avg_err = 0;
    // double max_err = 0, min_err = 0x3f3f3f;
    for (int i = 0; i < k; ++i) {
        int len = data[i].clu_point.size();
        double clu_err = 0;
        for (int r = 0; r < num_ref; ++r) {
            double err = 0;
            vector<double> arr_dis;
            arr_dis.reserve(len);
            for (int l = 0; l < len; ++l)
                arr_dis.push_back(CaculateEuclideanDis(ref_data[i][r], data[i].clu_point[l]));
            sort(arr_dis.begin(), arr_dis.end());
            // cout << arr_dis[0] << " "<< arr_dis[len-1] <<  "       ";
            if (!r)
                dist_max[i] = arr_dis[len - 1];

            double sum_xy = 0, sum_x = 0, sum_y = 0, sum_pow2x = 0;
            for (int y = 0; y < len; ++y) {
                sum_x += arr_dis[y];
                sum_y += y;
                sum_pow2x += arr_dis[y] * arr_dis[y];
                sum_xy += arr_dis[y] * y;
            }
            double avg_x = sum_x / len;
            double avg_y = sum_y / len;
            double avg_xy = sum_xy / len;
            double avg_pow2x = sum_pow2x / len;
            double a = (avg_xy - avg_x * avg_y) / (avg_pow2x - avg_x * avg_x);
            double b = avg_y - a * avg_x;
            // cout << a << " " << b << " ";
            // double max_err = 0, min_err = 0x3f3f3f;
            // vector<int> err_arr;
            // err_arr.resize(len);
            for (int c = 0; c < len; ++c) {
                int cal_y = (int) (a * arr_dis[c] + b);
                // err_arr[c] = abs(cal_y - c);
                // max_err = max_err > err_arr[c] ? max_err : err_arr[c];
                // min_err = min_err < err_arr[c] ? min_err : err_arr[c];
                err += abs(cal_y - c);
            }
            // cout << max_err << " " << min_err << endl;
            // for(int c = 0; c < len; ++c){
            //     err += (err_arr[c]-min_err) / (max_err - min_err);
            // }
            // cout << err / len << endl;

            // clu_err = clu_err + err / len;
            clu_err = clu_err + err;
        }
        // clu_err /= num_ref;
        // max_err = max_err > clu_err ? max_err : clu_err;
        // min_err = min_err < clu_err ? min_err : clu_err;
        // avg_err += clu_err * len / num_alldata;
        avg_err += clu_err;
    }
    // avg_err /= k;
    // cout << min_err << " " << max_err  << " " << avg_err << endl;
    // return (avg_err - min_err)/(max_err- min_err);
    // return avg_err;

    return avg_err / (num_ref * num_alldata);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double CalSihCoe(vector<Clu_Point> &data, int k) {
    double s = 0;
    double num_data = 0;
    for (int i = 0; i < k; ++i) {
        // cout << i << " ";
        int len = data[i].clu_point.size();
        num_data += len;

        for (int j = 0; j < len; ++j) {
            double a = 0, b = 0x3f3f3f;

            for (int k = 0; k < len; ++k)
                a += CaculateEuclideanDis(data[i].clu_point[j], data[i].clu_point[k]);

            a = a / (len - 1);

            for (int l = 0; l < k; ++l) {
                if (l == i)
                    continue;
                double bi = 0;
                int oth_len = data[l].clu_point.size();
                for (int o = 0; o < oth_len; ++o)
                    bi += CaculateEuclideanDis(data[i].clu_point[j], data[l].clu_point[o]);
                bi /= oth_len;
                b = b < bi ? b : bi;
            }
            s = s + (b - a) / (a < b ? b : a);
        }
    }
    return s / num_data;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double CalOverlapRate(vector<vector<Point> > &ref_data, vector<double> &dist_max, int k) {
    // 初始化重叠率 `overlap_rate` 为0，后续会不断累加每个簇与其他簇之间的重叠情况相关计算结果，并最终计算平均重叠率，用于存储最终要返回的整体重叠率值
    double overlap_rate = 0;
    // 通过循环遍历每个聚类簇（从 `i = 0` 到 `i < k`，这里 `k` 表示聚类的簇数量，是函数的传入参数），进行以下操作，计算当前簇与其他簇之间的重叠情况，并汇总到整体重叠率中
    for (int i = 0; i < k; ++i) {
        // 初始化当前簇与其他簇之间的重叠率相关变量 `rate` 为0，用于在后续循环中累加当前簇与其他每个簇之间的重叠程度计算结果，最后得到当前簇总的重叠程度相关值
        double rate = 0;
        // 通过循环遍历除当前簇之外的其他每个聚类簇（从 `j = 0` 到 `j < k`，但当 `i == j` 时跳过，即不与自身比较），进行以下操作，计算当前簇与每个其他簇之间的重叠情况
        for (int j = 0; j < k; ++j) {
            // 如果当前簇索引 `i` 与正在遍历的簇索引 `j` 相等，说明是同一个簇，不需要计算重叠情况，直接跳过本次循环，继续下一个簇的比较
            if (i == j)
                continue;
            // 计算当前簇（索引为 `i`）的参考数据集中第一个数据点（`ref_data[i][0]`）与正在比较的簇（索引为 `j`）的参考数据集中第一个数据点（`ref_data[j][0]`）之间的欧几里得距离，通过调用 `CaculateEuclideanDis` 函数进行计算，将结果赋值给 `dist_cl1_cl2` 变量，这个距离用于后续判断两个簇之间的相对位置关系和重叠可能性等情况
            double dist_cl1_cl2 = CaculateEuclideanDis(ref_data[i][0], ref_data[j][0]);

            // 如果两个簇的参考数据集首个数据点之间的距离 `dist_cl1_cl2` 大于当前簇（索引为 `i`）的最大距离值（`dist_max[i]`）与正在比较的簇（索引为 `j`）的最大距离值（`dist_max[j]`）之和，说明两个簇距离较远，基本没有重叠，此时将重叠率相关变量 `rate` 累加0（即不增加重叠程度）
            if (dist_cl1_cl2 > dist_max[i] + dist_max[j])
                rate += 0;
                // 如果两个簇的参考数据集首个数据点之间的距离不满足上述较远的条件，说明可能存在重叠情况，按照以下特定的计算公式计算重叠程度，并累加到 `rate` 变量中，这里的计算公式涉及到比较复杂的距离关系和取最值操作，目的是根据两个簇的相对位置以及各自的最大距离范围来量化重叠程度
            else
                rate = rate + (min(dist_cl1_cl2 + dist_max[j], dist_max[i]) - max(dist_cl1_cl2 - dist_max[j], 0.0)) /
                              dist_max[i];

        }
        // 将当前簇与其他簇之间的重叠程度相关值 `rate` 除以簇数量减1（`k - 1`，因为自身不参与比较，所以总共比较的簇数量是 `k - 1` 个），得到当前簇与其他簇平均的重叠程度，然后累加到 `overlap_rate` 变量中，用于汇总所有簇的重叠情况
        overlap_rate = overlap_rate + rate / (k - 1);
    }
    // 将汇总的所有簇的重叠情况相关值 `overlap_rate` 除以簇数量 `k`，得到平均的重叠率，即最终的整体重叠率，用于衡量聚类结果中不同簇之间的重叠程度，反映聚类的区分效果，然后返回该值
    return overlap_rate / k;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double CalR2(vector<Clu_Point> &data, vector<vector<Point> > &ref_data, int k, int num_ref) {
    double r2 = 0;
    for (int i = 0; i < k; ++i) {
        int len = data[i].clu_point.size();
        double clu_r2 = 0;
        for (int r = 0; r < num_ref; ++r) {
            vector<double> arr_dis;
            arr_dis.reserve(len);
            for (int l = 0; l < len; ++l)
                arr_dis.push_back(CaculateEuclideanDis(ref_data[i][r], data[i].clu_point[l]));
            sort(arr_dis.begin(), arr_dis.end());

            // cout << arr_dis[0] << "   "<< arr_dis[len-1] << endl;

            double sum_xy = 0, sum_x = 0, sum_y = 0, sum_pow2x = 0;
            for (int y = 0; y < len; ++y) {
                sum_x += arr_dis[y];
                sum_y += y;
                sum_pow2x += arr_dis[y] * arr_dis[y];
                sum_xy += arr_dis[y] * y;
            }
            double avg_x = sum_x / len;
            double avg_y = sum_y / len;
            double avg_xy = sum_xy / len;
            double avg_pow2x = sum_pow2x / len;
            double a = (avg_xy - avg_x * avg_y) / (avg_pow2x - avg_x * avg_x);
            double b = avg_y - a * avg_x;

            double sum = 0, sum_var = 0;

            for (int c = 0; c < len; ++c) {
                int cal_y = (int) (a * arr_dis[c] + b);
                sum += pow(c - cal_y, 2);
                sum_var += pow(c - avg_y, 2);
            }
            sum /= len;
            sum_var /= len;
            clu_r2 = clu_r2 + 1 - sum / sum_var;
        }
        clu_r2 /= num_ref;
        // cout << clu_r2 << endl;

        r2 += clu_r2;
    }
    r2 /= k;
    return r2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, const char *argv[]) {

    string filename_main = "../../results/lims/main_out.txt";
    std::ofstream outFile(filename_main, std::ios::app);
    if (outFile.is_open()) {
        outFile << "*******************************Start************************************" << std::endl;

        srand((int) time(0));
        // begining
        if (argc < 5) {
            outFile
                    << "Plz follow format of : ./main [number of reference point] [number of cluster] [dimention of point] [evaluate the clustering or not(0 /1 : [min number of cluster] [max number of cluster] [step size])]"
                    << endl;
            return 0;
        }

        unsigned num_ref = stoi(argv[1]);
        unsigned num_clu = stoi(argv[2]);
        unsigned dim = stoi(argv[3]);
        unsigned flag_eva = stoi(argv[4]);


        if (num_ref > dim + 1) {
            outFile
                    << "The number of reference point should be smaller than dimentional of point data. Plz reinput parameter"
                    << endl;
            return 0;
        }

        // Pretreatment : clustering and cluster evaluation
        if (flag_eva) {
            unsigned min_clu = stoi(argv[5]);
            unsigned max_clu = stoi(argv[6]);
            unsigned step = stoi(argv[7]);

            string data_path = "../../dataset/forest_original_100000.txt";
            for (unsigned i = min_clu; i < max_clu + 1; i += step) {

                pnum = i;    // 簇个数
                readm(data_path, dim, 0);
                int num_alldata = num;    // 总的数据量
                outFile << num_alldata << endl;    // 输出总的数据量
                for (int k = 0; k <= num; k++)fgg[k] = 0;   // 标记为未标记状态
                work(dim);    // 开始聚簇

                // 指明簇文件存放路径
                string clu_data_path = "../../dataset/lims/outputFiles/K_" + to_string(i);
                int isCreate = mkdir(clu_data_path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO);
                if (!isCreate)
                    outFile << "create path success : " << clu_data_path << endl;
                else {
                    outFile << "create path failed ! error code :  " << isCreate << clu_data_path << endl;
                    return 0;
                }

                op(clu_data_path + "/ref.txt", dim);   ///存放每个簇的簇心
                clu(dim, clu_data_path);        // 簇和簇心划分完毕
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // 从每个簇里再找出几个枢轴点
                pnum = num_ref;
                for (unsigned j = 0; j < i; ++j) {
                    string clu_path = clu_data_path + "/clu_" + to_string(j) + ".txt";
                    readm(clu_path, dim, 1);
                    for (int k = 0; k <= num; k++)fgg[k] = 0;
                    // 从每个簇中找枢轴点
                    work(dim);
                    // 保存每个簇的参考点
                    op(clu_data_path + "/ref_" + to_string(j) + ".txt", dim);
                }
                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // reread cluster result data file and caculate average err and silhouette coefficient
                vector<Clu_Point> data;
                data.reserve(i);
                vector<vector<Point> > ref_data;    // 簇心 + 每个簇内的枢轴点
                ref_data.resize(i);

                string ref_path = clu_data_path + "/ref.txt";    // k个簇心
                ifstream fin;
                fin.open(ref_path);
                if (!fin) {
                    outFile << ref_path << " file could not be opened\n";
                    exit(0);
                }
                string line;
                int index = 0;
                while (getline(fin, line)) {
                    stringstream ss(line);
                    string value;
                    vector<double> coor;
                    while (getline(ss, value, ',')) {
                        coor.push_back(atof(value.c_str()));
                    }
                    Point main_ref = Point(coor);
                    ref_data[index++].push_back(main_ref);
                }
                fin.close();

                // 循环读取每个簇数据
                for (unsigned j = 0; j < i; ++j) {
                    InputReader inputReader(clu_data_path + "/clu_" + to_string(j) + ".txt");
                    data.push_back(inputReader.getCluster());

                    string oth_ref_path = clu_data_path + "/ref_" + to_string(j) + ".txt";
                    fin.open(oth_ref_path);
                    if (!fin) {
                        outFile << oth_ref_path << " file could not be opened\n";
                        exit(0);
                    }
                    while (getline(fin, line)) {
                        stringstream ss(line);
                        string value;
                        vector<double> coor;
                        while (getline(ss, value, ',')) {
                            coor.push_back(atof(value.c_str()));
                        }
                        Point oth_ref = Point(coor);
                        ref_data[j].push_back(oth_ref);
                    }
                    fin.close();
                }

                vector<double> dist_max;
                dist_max.resize(i);

                // double s = CalSihCoe(data,i);
                double avg_err = CalErrAvg(data, ref_data, dist_max, i, num_ref, num_alldata);
                double rate = CalOverlapRate(ref_data, dist_max, i);
                double r2 = CalR2(data, ref_data, i, num_ref);

                // outFile << "K is : " << i << " Silhouette Coefficient : " << s
                // << " average err of RP model : " << avg_err << " overlap rate is : " << rate << endl;

                outFile << "K is : " << i << " average err of RP model : "
                        << avg_err << " overlap rate is : " << rate << " r2 is :" << r2 << endl;

            }

            return 0;
        }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // all data
        vector<Clu_Point> all_data;
        vector<mainRef_Point> all_refSet;

        vector<Point> pivots;    // 存放的是簇心
        pivots.reserve(num_clu);
        vector<vector<Point>> oth_pivots;    // 存放的是每个簇里的枢轴点
        oth_pivots.reserve(num_clu);

        string filename = "../../dataset/lims/inputFiles/ref/ref.txt";    // 指定簇心文件
        ifstream fin;
        fin.open(filename);
        if (!fin) {
            outFile << filename << " file could not be opened\n";
            exit(0);
        }
        string line;
        while (getline(fin, line)) {
            stringstream ss(line);
            string value;
            vector<double> coordinate;
            while (getline(ss, value, ',')) {
                coordinate.push_back(atof(value.c_str()));
            }
            Point pivot_pt = Point(coordinate);
            pivots.push_back(pivot_pt);
        }
        fin.close();

        for (unsigned p = 0; p < num_clu; ++p) {
            string filename = "../../dataset/lims/inputFiles/ref/ref_" + to_string(p) + ".txt";
            ifstream fin;
            fin.open(filename);
            if (!fin) {
                outFile << filename << " file could not be opened\n";
                exit(0);
            }

            string line;
            vector<Point> other_pivot;
            while (getline(fin, line)) {
                stringstream ss(line);
                string value;
                vector<double> coordinate;
                while (getline(ss, value, ',')) {
                    coordinate.push_back(atof(value.c_str()));
                }
                Point pivot_pt = Point(coordinate);
                other_pivot.push_back(pivot_pt);
            }
            oth_pivots.push_back(other_pivot);
            fin.close();
        }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        double build_time = 0.0;
        // 循环加载每个簇中的数据
        for (unsigned i = 0; i < num_clu; i++) {
            InputReader inputReader("../../dataset/lims/inputFiles/clu/clu_" + to_string(i) + ".txt");
            // 保存一个簇中的数据
            all_data.push_back(inputReader.getCluster());

            if (all_data[i].clu_point.empty()) {
                outFile << "Plz do not load a null file" << endl;
                return 0;
            }

            chrono::steady_clock::time_point begin = chrono::steady_clock::now();

            // 设定参考点及其模型参数
            ChooseRef ref_point(num_ref - 1, all_data[i], pivots[i], oth_pivots[i], 1);

            CaculateIValue calIValue(all_data[i], ref_point.getMainRefPoint());

            chrono::steady_clock::time_point end = chrono::steady_clock::now();

            build_time += chrono::duration_cast<chrono::milliseconds>(end - begin).count();

            all_data[i] = calIValue.getCluster();
            all_refSet.push_back(calIValue.getMainRef_Point());
        }

        chrono::steady_clock::time_point begin = chrono::steady_clock::now();
        for (unsigned i = 0; i < num_clu; i++) {
            OutputPrinter output(i, all_refSet[i]);
        }
        chrono::steady_clock::time_point end = chrono::steady_clock::now();

        build_time += chrono::duration_cast<chrono::milliseconds>(end - begin).count();

        outFile << "build time is : " << build_time << endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // range query
        double time = 0.0;
        int page = 0;

        string rangeQuery_filename = "../../dataset/lims/inputFiles/range.txt";
        vector<Point> list_rangeQry = LoadPointForQuery(rangeQuery_filename);
        double r = 0.2;
        time = 0.0;
        double io_time = 0.0;
        int queryPt_num = 0;
        page = 0;
        for (auto &m: list_rangeQry) {       // 遍历每个查询点

            vector<int> rangeQueryRes;
            rangeQueryRes.reserve(10000);

            chrono::steady_clock::time_point begin = chrono::steady_clock::now();

            for (unsigned i = 0; i < num_clu; ++i) {    // 遍历每个簇

                // 首先判断查询圆与参考点最大圆之间的关系（圆与圆间的关系）
                CalCirclePos mainRefPtCircle(all_refSet[i].point, all_refSet[i].r, m, r);
                if (mainRefPtCircle.label == 1) {    // 若外切+外离：直接不看
                    continue;
                }

                // 如果内含，但是是内含查询圆：也直接不看
                if (mainRefPtCircle.label == 3 && mainRefPtCircle.dis_upper < all_refSet[i].r_low) {
                    continue;
                }

                // 大圆内含小圆，同时查询圆半径大于簇最大半径，该簇所有结果均满足
                if (mainRefPtCircle.label == 3 && r > all_refSet[i].r) {

                    ifstream fin;
                    int start_page = 0;
                    int end_page = all_refSet[i].iValuePts.size() / Constants::PAGE_SIZE;

                    for (int s = start_page; s <= end_page; ++s) {
                        vector<vector<double> > pt_page;
                        string fileName = "./data/cluster_" + to_string(i) + "_" + to_string(s);
                        ++page;
                        fin.open(fileName, ios::binary);
                        int size_page;
                        fin.read((char *) &size_page, 4);
                        pt_page.reserve(size_page);
                        int size_dim;
                        fin.read((char *) &size_dim, 4);
                        for (int q = 0; q < size_page; q++) {
                            vector<double> coordinate;
                            coordinate.resize(size_dim);
                            fin.read(reinterpret_cast<char *>(&coordinate), sizeof(coordinate));
                            pt_page.push_back(coordinate);
                        }
                        fin.close();

                        for (int l = 0 + s * Constants::PAGE_SIZE; l < 0 + s * Constants::PAGE_SIZE + size_page; ++l)
                            rangeQueryRes.push_back(all_refSet[i].iValuePts[l].id);
                        // rangeQueryRes.insert(rangeQueryRes.end(), all_refSet[i].iValuePts.begin()+s*Constants::PAGE_SIZE, all_refSet[i].iValuePts.begin()+s*Constants::PAGE_SIZE+size_page);
                    }

                    continue;
                }

                // 簇内数据部分满足
                bool flag = true;
                vector<CalCirclePos> ref_query;
                ref_query.reserve(num_ref - 1);
                for (unsigned j = 0; j < num_ref - 1; ++j) {
                    CalCirclePos RefPtCircle(all_refSet[i].ref_points[j].point, all_refSet[i].ref_points[j].r,
                                             m, r);
                    if (RefPtCircle.label == 1) {
                        flag = false;
                        break;
                    }
                    ref_query.push_back(RefPtCircle);
                }

                if (!flag)
                    continue;

                RangeQuery rangeQuery(m, r, all_refSet[i], rangeQueryRes, mainRefPtCircle, ref_query);
            }

            chrono::steady_clock::time_point end = chrono::steady_clock::now();

            time += chrono::duration_cast<chrono::microseconds>(end - begin).count();
            queryPt_num += rangeQueryRes.size();
            outFile << "该轮查询结果数: " << rangeQueryRes.size() << ", 执行耗时: "
                    << chrono::duration_cast<chrono::microseconds>(end - begin).count() << " [µs]" << endl;
        }
        time /= list_rangeQry.size();
        io_time /= list_rangeQry.size();
        queryPt_num /= list_rangeQry.size();
        page /= list_rangeQry.size();
        outFile << "range query LIMS index time is : " << time << " [µs]" << endl;
        outFile << "io time : " << io_time << " [µs]" << endl;
        outFile << "query point number avg is : " << queryPt_num << endl;
        outFile << "avg page : " << page << endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        unsigned K = 5;

        filename = "../../dataset/lims/inputFiles/KNN.txt";
        vector<Point> list_KNN = LoadPointForQuery(filename);

        for (unsigned m = 0; m < list_KNN.size(); ++m) {
            // 初始半径
            double init_r = 0.05;
            // 递增半径
            double delta_r = 0.05;

            // 标志位数组，判断每个点检查过没有
            int8_t arr[num_clu][length_arr] = {0};
            chrono::steady_clock::time_point begin = chrono::steady_clock::now();

            priority_queue<pair<double, unsigned int> > KNNRes_queue;

            while (KNNRes_queue.size() < K || KNNRes_queue.top().first > init_r + delta_r) {

                for (unsigned i = 0; i < num_clu; ++i) {
                    CalCirclePos mainRefPtCircle(all_refSet[i].point, all_refSet[i].r, list_KNN[m], init_r);
                    if (mainRefPtCircle.label == 1) {
                        continue;
                    }
                    if (mainRefPtCircle.label == 3 && mainRefPtCircle.dis_upper < all_refSet[i].r_low) {
                        continue;
                    }
                    if (mainRefPtCircle.label == 3 && init_r > all_refSet[i].r) {
                        for (unsigned j = 0; j < all_refSet[i].iValuePts.size(); ++j) {
                            double dis_pt_qrpt = CaculateEuclideanDis(all_refSet[i].iValuePts[j], list_KNN[m]);
                            if (KNNRes_queue.size() < K) {
                                arr[i][j] = 1;
                                pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[j].id);
                                KNNRes_queue.push(elem);
                            } else {
                                if (KNNRes_queue.top().first > dis_pt_qrpt && arr[i][j] == 0) {
                                    arr[i][j] = 1;
                                    KNNRes_queue.pop();
                                    pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[j].id);
                                    KNNRes_queue.push(elem);
                                }
                            }
                        }
                        continue;
                    }

                    bool flag = true;
                    vector<CalCirclePos> ref_query;
                    ref_query.reserve(num_ref - 1);
                    for (unsigned j = 0; j < num_ref - 1; ++j) {
                        CalCirclePos RefPtCircle(all_refSet[i].ref_points[j].point, all_refSet[i].ref_points[j].r,
                                                 list_KNN[m], init_r);
                        if (RefPtCircle.label == 1) {
                            flag = false;
                            break;
                        }
                        if (RefPtCircle.label == 3 && RefPtCircle.dis_upper < all_refSet[i].ref_points[j].r_low) {
                            flag = false;
                            break;
                        }
                        if (RefPtCircle.label == 3 && init_r > all_refSet[i].ref_points[j].r) {
                            for (unsigned l = 0; l < all_refSet[i].iValuePts.size(); ++l) {
                                double dis_pt_qrpt = CaculateEuclideanDis(all_refSet[i].iValuePts[l], list_KNN[m]);
                                if (KNNRes_queue.size() < K) {
                                    arr[i][l] = 1;
                                    pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[l].id);
                                    KNNRes_queue.push(elem);
                                } else {
                                    if (KNNRes_queue.top().first > dis_pt_qrpt && arr[i][l] == 0) {
                                        arr[i][l] = 1;
                                        KNNRes_queue.pop();
                                        pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[l].id);
                                        KNNRes_queue.push(elem);
                                    }
                                }
                            }
                            break;
                        }
                        ref_query.push_back(RefPtCircle);
                    }

                    if (!flag)
                        continue;

                    KNN KNNQuery(list_KNN[m], all_refSet[i], KNNRes_queue, mainRefPtCircle, ref_query, K, arr, i);
//                KNN_IO KNNQuery(list_KNN[m], all_refSet[i], KNNRes_queue, mainRefPtCircle, ref_query, K, arr, i, page);
                    // OutputPrinter outputPrinter2;
                    // outputPrinter2.print_ivalue(outputFileName + "ivalue.txt", all_refSet[i].iValuePts);
                }

                init_r += delta_r;
            }

            // 最后一轮准确范围查询
            for (unsigned i = 0; i < num_clu; ++i) {
                CalCirclePos mainRefPtCircle(all_refSet[i].point, all_refSet[i].r, list_KNN[m], init_r);
                if (mainRefPtCircle.label == 1) {
                    continue;
                }
                if (mainRefPtCircle.label == 3 && mainRefPtCircle.dis_upper < all_refSet[i].r_low) {
                    continue;
                }
                if (mainRefPtCircle.label == 3 && init_r > all_refSet[i].r) {
                    for (unsigned j = 0; j < all_refSet[i].iValuePts.size(); ++j) {
                        double dis_pt_qrpt = CaculateEuclideanDis(all_refSet[i].iValuePts[j], list_KNN[m]);
                        if (KNNRes_queue.size() < K) {
                            arr[i][j] = 1;
                            pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[j].id);
                            KNNRes_queue.push(elem);
                        } else {
                            if (KNNRes_queue.top().first > dis_pt_qrpt && arr[i][j] == 0) {
                                KNNRes_queue.pop();
                                pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[j].id);
                                KNNRes_queue.push(elem);
                            }
                        }
                    }
                    continue;
                }


                bool flag = true;
                vector<CalCirclePos> ref_query;
                ref_query.reserve(num_ref - 1);
                for (unsigned j = 0; j < num_ref - 1; ++j) {
                    CalCirclePos RefPtCircle(all_refSet[i].ref_points[j].point, all_refSet[i].ref_points[j].r,
                                             list_KNN[m],
                                             init_r);
                    if (RefPtCircle.label == 1) {
                        flag = false;
                        break;
                    }
                    if (RefPtCircle.label == 3 && RefPtCircle.dis_upper < all_refSet[i].ref_points[j].r_low) {
                        flag = false;
                        break;
                    }
                    if (RefPtCircle.label == 3 && init_r > all_refSet[i].ref_points[j].r) {
                        for (unsigned l = 0; l < all_refSet[i].iValuePts.size(); ++l) {
                            double dis_pt_qrpt = CaculateEuclideanDis(all_refSet[i].iValuePts[l], list_KNN[m]);
                            if (KNNRes_queue.size() < K) {
                                arr[i][l] = 1;
                                pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[l].id);
                                KNNRes_queue.push(elem);
                            } else {
                                if (KNNRes_queue.top().first > dis_pt_qrpt && arr[i][l] == 0) {
                                    arr[i][l] = 1;
                                    KNNRes_queue.pop();
                                    pair<double, unsigned int> elem(dis_pt_qrpt, all_refSet[i].iValuePts[l].id);
                                    KNNRes_queue.push(elem);
                                }
                            }
                        }
                        break;
                    }
                    ref_query.push_back(RefPtCircle);
                }

                if (!flag)
                    continue;

                KNN KNNQuery(list_KNN[m], all_refSet[i], KNNRes_queue, mainRefPtCircle, ref_query, K, arr, i);
//            KNN_IO KNNQuery(list_KNN[m], all_refSet[i], KNNRes_queue, mainRefPtCircle, ref_query, K, arr, i, page);
            }

            chrono::steady_clock::time_point end = chrono::steady_clock::now();

            time += chrono::duration_cast<chrono::microseconds>(end - begin).count();

        }

        time /= list_KNN.size();
        page /= list_KNN.size();
        outFile << " KNN LIMS index time is : " << time << " [µs]" << endl;
        outFile << "avg page : " << page << endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // // test pointquery
        // double time = 0.0;
        // int page = 0;
        // int acc = 0;
        // // filename = "/home/qrstu/yty/dataset/syn/guass/range_0.01_2d/point.txt";
        // // vector<Point> list_point = LoadPointForQuery(filename);

        // for(unsigned o = 0; o < 200; ++o){
        //     int cl = random(0,49);
        //     int pt = random(0,100);
        //     vector<double> queryPt;
        //     for(int d = 0; d < dim; ++d){
        //         double coor = random(0,100000) / 100000.0;
        //         queryPt.push_back(coor);
        //     }
        //     Point point = Point(queryPt);

        //     chrono::steady_clock::time_point begin = chrono::steady_clock::now();
        //     int query_res = -1;
        //     // double dis = CaculateEuclideanDis(all_data[cl].clu_point[pt], all_refSet[0].point);
        //     double dis = CaculateEuclideanDis(point, all_refSet[0].point);
        //     int clu_id = 0;
        //     for(unsigned i = 1; i < num_clu; ++i){
        //         // double dis_other = CaculateEuclideanDis(all_data[cl].clu_point[pt], all_refSet[i].point);
        //         double dis_other = CaculateEuclideanDis(point, all_refSet[i].point);
        //         if(dis_other < dis){
        //             dis = dis_other;
        //             clu_id = i;
        //         }
        //     }
        //     // PointQuery_IO pointQuery_IO(all_data[cl].clu_point[pt], all_refSet[clu_id], query_res,clu_id,dis,page);
        //     PointQuery_IO pointQuery_IO(point, all_refSet[clu_id], query_res,clu_id,dis,page);
        //     chrono::steady_clock::time_point end = chrono::steady_clock::now();
        //     time += chrono::duration_cast<chrono::nanoseconds>(end - begin).count();
        //     if(query_res!= -1)
        //         acc++;
        // }
        // time /= 200;
        // outFile << "point query LIMS index avg time : " << time << "[ns]" << endl;
        // outFile << "page avg : " << page<< endl;
        // outFile << "acc " <<  acc << endl;






        // test insert and query

        // string insert_file = "/home/qrstu/yty/LIMS/inputFiles/insert/skew_0.001.txt";
        // Insert(insert_file,all_refSet,num_clu);


        // rangeQuery_filename = "/home/qrstu/yty/LIMS/inputFiles/range.txt";
        // list_rangeQry = LoadPointForQuery(rangeQuery_filename);
        // r = 0.2;
        // time = 0.0;
        // io_time = 0.0;
        // queryPt_num = 0;
        // page = 0;
        // for(unsigned m = 0; m < list_rangeQry.size(); ++m){

        //     vector<int> rangeQueryRes;
        //     rangeQueryRes.reserve(100000);

        //     chrono::steady_clock::time_point begin = chrono::steady_clock::now();

        //     for(unsigned i = 0; i < num_clu; ++i){
        //         CalCirclePos mainRefPtCircle(all_refSet[i].point, all_refSet[i].r, list_rangeQry[m], r);
        //         if(mainRefPtCircle.label == 1){
        //             continue;
        //         }
        //         if(mainRefPtCircle.label == 3 && mainRefPtCircle.dis_upper < all_refSet[i].r_low){
        //             continue;
        //         }
        //         if(mainRefPtCircle.label == 3 && r > all_refSet[i].r){

        //             ifstream fin;
        //             int start_page = 0;
        //             int end_page = all_refSet[i].iValuePts.size() / Constants::PAGE_SIZE;

        //             for(int s = start_page; s <= end_page; ++s){
        //                 vector<vector<double> > pt_page;
        //                 string fileName = "./data/cluster_" + to_string(i) + "_" + to_string(s);
        //                 fin.open(fileName, ios::binary);
        //                 int size_page;
        //                 fin.read((char*)&size_page,4);
        //                 pt_page.reserve(size_page);
        //                 int size_dim;
        //                 fin.read((char*)&size_dim,4);
        //                 for(int q = 0 ; q < size_page; q++){
        //                     vector<double> coordinate;
        //                     coordinate.resize(size_dim);
        //                     fin.read(reinterpret_cast<char*>(&coordinate), sizeof(coordinate));
        //                     pt_page.push_back(coordinate);
        //                 }
        //                 fin.close();
        //                 for(int l = 0+s*Constants::PAGE_SIZE; l < 0+s*Constants::PAGE_SIZE+size_page; ++l)
        //                     rangeQueryRes.push_back(all_refSet[i].iValuePts[l].id);
        //             }
        //             continue;
        //         }


        //         double dis_qrpt_mainRef = CaculateEuclideanDis(all_refSet[i].point,list_rangeQry[m]);
        //         int insert_pt_size = all_refSet[i].insert_list.size();
        //         double lower = 0.0;
        //         double upper = 0.0;
        //         int low_pos = -1;
        //         int up_pos = -1;
        //         if(dis_qrpt_mainRef - r < all_refSet[i].insert_list[insert_pt_size-1].i_value){
        //             if(dis_qrpt_mainRef - r < 0){
        //                 lower = 0;
        //                 low_pos = 0;
        //             }else{
        //                 lower = dis_qrpt_mainRef - r;
        //             }

        //             if(dis_qrpt_mainRef + r < all_refSet[i].insert_list[insert_pt_size-1].i_value){
        //                 upper = dis_qrpt_mainRef + r;
        //             }else{
        //                 upper = all_refSet[i].insert_list[insert_pt_size-1].i_value;
        //                 up_pos = insert_pt_size-1;
        //             }

        //             if(low_pos == -1)
        //                 low_pos = BinarySearch(all_refSet[i].insert_list,lower,0,insert_pt_size);
        //             if(up_pos == -1)
        //                 up_pos = BinarySearch(all_refSet[i].insert_list,upper,0,insert_pt_size);



        //             int start_page = low_pos / Constants::PAGE_SIZE;
        //             int end_page = up_pos / Constants::PAGE_SIZE;



        //             for(int s = start_page; s <= end_page; ++s){
        //                 ++page;
        //                 vector<vector<double> > pt_page;
        //                 chrono::steady_clock::time_point start = chrono::steady_clock::now();
        //                 string fileName = "./data/cluster_" + to_string(i) + "_insert_" + to_string(s);
        //                 // outFile << fileName << endl;
        //                 fin.open(fileName, ios::binary);
        //                 int size_page;
        //                 fin.read((char*)&size_page,4);
        //                 pt_page.reserve(size_page);
        //                 int size_dim;
        //                 fin.read((char*)&size_dim,4);
        //                 for(int q = 0 ; q < size_page; q++){
        //                     vector<double> coordinate;
        //                     coordinate.resize(size_dim);
        //                     fin.read(reinterpret_cast<char*>(coordinate.data()),size_dim * sizeof(coordinate.front()));
        //                     pt_page.push_back(coordinate);
        //                 }
        //                 fin.close();
        //                 chrono::steady_clock::time_point end = chrono::steady_clock::now();
        //                 io_time+=chrono::duration_cast<chrono::microseconds>(end - start).count();
        //                 for(int x = 0; x < size_page; ++x){
        //                     if(CaculateEuclideanDis2(pt_page[x], list_rangeQry[m]) <= r){
        //                         rangeQueryRes.push_back(all_refSet[i].insert_list[s*Constants::PAGE_SIZE+x].id);
        //                     }
        //                 }
        //             }



        //             // for(; low_pos <= up_pos; ++low_pos){
        //             //     if(CaculateEuclideanDis2(all_refSet[i].insert_list[low_pos].coordinate,list_rangeQry[m]) <= r)
        //             //         rangeQueryRes.push_back(all_refSet[i].insert_list[low_pos].id);
        //             // }
        //         }



        //         bool flag = true;
        //         vector<CalCirclePos> ref_query;
        //         ref_query.reserve(num_ref - 1);
        //         for(unsigned j = 0; j < num_ref - 1; ++j){
        //             CalCirclePos RefPtCircle(all_refSet[i].ref_points[j].point, all_refSet[i].ref_points[j].r, list_rangeQry[m], r);
        //             if(RefPtCircle.label == 1){
        //                 flag = false;
        //                 break;
        //             }
        //             ref_query.push_back(RefPtCircle);
        //         }

        //         if(!flag)
        //             continue;



        //         RangeQuery_IO rangeQuery( list_rangeQry[m], r, all_refSet[i], rangeQueryRes, mainRefPtCircle, ref_query,i,page,io_time);
        //     }
        //     chrono::steady_clock::time_point end = chrono::steady_clock::now();

        //     time += chrono::duration_cast<chrono::microseconds>(end - begin).count();
        //     queryPt_num += rangeQueryRes.size();
        // }
        // time /= list_rangeQry.size();
        // io_time /= list_rangeQry.size();
        // queryPt_num /= list_rangeQry.size();
        // page /= list_rangeQry.size();
        // outFile << "range query LIMS index time is : " << time << " [µs]" <<  endl;
        // outFile << "io time : " << io_time << " [µs]" << endl;
        // outFile << "query point number avg is : " << queryPt_num << endl;
        // outFile << "avg page : " << page << endl;

        outFile << "********************************Over************************************" << std::endl << std::endl;
    }

    return 0;
}
